using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;

using Microsoft.CSharp;
using Microsoft.VisualStudio.TestTools.UITesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using FluentAssertions;

namespace CodeStream.Testing.WebAutomation.UnitTests
{
    internal static class UITemplateTestExtensions
    {
        public static Type ShouldHaveType(this Assembly assembly, string typeName, Type baseType = null)
        {
            var pageType = assembly.GetTypes().FirstOrDefault(type => type.FullName == typeName);
            pageType.Should().NotBeNull("Could not find a class with a full name '{0}'", typeName);

            if (baseType != null)
                pageType.BaseType.Should().Be(baseType);

            return pageType;
        }

        public static PropertyInfo ShouldHaveProperty(this Type type, string name)
        {
            var propertyInfo = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
            propertyInfo.Should().NotBeNull("Could not find a property with a name of '{0}'", name);
            return propertyInfo;
        }

        public static MethodInfo ShouldHaveMethod(this Type type, string name)
        {
            var methodInfo = type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
            methodInfo.Should().NotBeNull("Could not find a method with a name of '{0}'", name);
            return methodInfo;
        }

        public static PropertyExpression ShouldHaveProperty(this PropertyExpressionCollection searchProperties, string name, PropertyExpressionOperator op, string value)
        {
            var searchExpression = searchProperties.FirstOrDefault(searchProperty => searchProperty.PropertyName == name);
            searchExpression.Should().NotBeNull("Could not find a search property for '{0}'", name);
            searchExpression.PropertyValue.Should().BeEquivalentTo(value);
            searchExpression.PropertyOperator.Should().Be(op);

            return searchExpression;
        }

        public static string GenerateDefinitionCode(Stream uiDefinition)
        {
            string generatedCode;
            using (XmlReader uiDefinitionReader = XmlReader.Create(uiDefinition))
                generatedCode = new UIDefinitionClassTemplate(uiDefinitionReader, "TestNamespace").TransformText();
            return generatedCode;
        }

        public static Assembly CompileCode(string code, string[] referencedAssemblies, string outputPath)
        {
            CompilerParameters compilerParameters = new CompilerParameters(referencedAssemblies)
            {
                OutputAssembly = outputPath
            };

            using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
            {
                var codeUnit = codeProvider.CompileAssemblyFromSource(compilerParameters, code);
                if (codeUnit.Errors.Count != 0)
                {
                    Debug.WriteLine(code);
                    foreach (var error in codeUnit.Errors.Cast<CompilerError>())
                        Debug.WriteLine("{0} {1}: {2}", error.IsWarning ? "Warning" : "Error", error.ErrorNumber, error.ErrorText);
                    Assert.Fail("There was an error compiling the code.");
                }

                return codeUnit.CompiledAssembly;
            }
        }
    }
}

